package pers.qianyu.module.image.service.impl;

import cn.hutool.core.util.ArrayUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import pers.qianyu.module.core.comm.Pagination;
import pers.qianyu.module.core.domain.image.converter.ImageConverter;
import pers.qianyu.module.core.domain.image.dto.ImageLikeDTO;
import pers.qianyu.module.core.domain.image.dto.NoticeDTO;
import pers.qianyu.module.core.domain.image.entity.ImageLikePO;
import pers.qianyu.module.core.domain.image.entity.ImagePO;
import pers.qianyu.module.core.domain.image.enums.NoticeTypeEnum;
import pers.qianyu.module.core.domain.image.query.ImageLikeQuery;
import pers.qianyu.module.core.domain.image.vo.ImageVO;
import pers.qianyu.module.image.dao.ImageDao;
import pers.qianyu.module.image.dao.ImageLikeDao;
import pers.qianyu.module.image.exception.ImageErrorCodeEnum;
import pers.qianyu.module.image.exception.ImageException;
import pers.qianyu.module.image.listener.events.LikeImageEvent;
import pers.qianyu.module.image.service.ImageLabelService;
import pers.qianyu.module.image.service.ImageLikeService;
import pers.qianyu.module.image.service.NoticeService;
import pers.qianyu.module.system.listener.events.UserInfoIdsUpdatedEvent;

import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author mizzle rain
 * @date 2021-02-09 11:09
 */
@Service("imageLikeService")
public class ImageLikeServiceImpl implements ImageLikeService {
    @Autowired
    private ImageDao imageDao;
    @Autowired
    private ImageLikeDao imageLikeDao;
    @Autowired
    private ImageConverter imageConverter;
    @Autowired
    private NoticeService noticeService;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private ImageLabelService imageLabelService;

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void insert(ImageLikeDTO imageLikeDTO) {
        // 更新点赞数
        ImagePO imagePO = imageDao.selectById(imageLikeDTO.getImageId());
        if (Objects.isNull(imagePO)) {
            throw new ImageException(ImageErrorCodeEnum.INVALID_IMAGE_ID);
        }
        imagePO.setLikes(imagePO.getLikes() + imageLikeDTO.getCount());
        imageDao.updateById(imagePO);

        // 插入记录
        ImageLikePO imageLikePO = new ImageLikePO();
        imageLikePO.setImageId(imageLikeDTO.getImageId());
        imageLikePO.setCreatedBy(imageLikeDTO.getCreatedBy());
        imageLikePO.setUpdatedBy(imageLikePO.getCreatedBy());
        imageLikePO.setCreateTime(System.currentTimeMillis());
        imageLikePO.setUpdateTime(imageLikePO.getCreateTime());
        imageLikeDao.insert(imageLikePO);

        // 插入一条通知
        applicationContext.publishEvent(new LikeImageEvent(this,
                imagePO, imageLikeDTO.getCreatedBy(), NoticeTypeEnum.LIKED));
        // 更新 Redis 用户信息（一定要在最后更新 Redis，否则事务回滚的话会读取到错误结果）
        List<Long> ids = findImageIdsByUserId(imageLikeDTO.getCreatedBy());
        applicationContext.publishEvent(new UserInfoIdsUpdatedEvent(this, ids,
                imageLikeDTO.getCreatedBy(), UserInfoIdsUpdatedEvent.Type.IMAGE_LIKE_IDS_UPDATED));
    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void delete(ImageLikeDTO imageLikeDTO) {
        // 更新点赞数
        ImagePO imagePO = imageDao.selectById(imageLikeDTO.getImageId());
        if (Objects.isNull(imagePO)) {
            throw new ImageException(ImageErrorCodeEnum.INVALID_IMAGE_ID);
        }
        imagePO.setLikes(imagePO.getLikes() - imageLikeDTO.getCount());
        imageDao.updateById(imagePO);

        // 删除记录
        ImageLikePO imageLikePO = new ImageLikePO();
        imageLikePO.setImageId(imageLikeDTO.getImageId());
        imageLikePO.setCreatedBy(imageLikeDTO.getCreatedBy());
        QueryWrapper<ImageLikePO> wrapper = new QueryWrapper<>(imageLikePO);
        imageLikeDao.delete(wrapper);

        // 如果通知的状态为未读，则删除对应的通知
        NoticeDTO noticeDTO = new NoticeDTO();
        noticeDTO.setCreatedBy(imageLikeDTO.getCreatedBy());
        noticeDTO.setType(NoticeTypeEnum.LIKED.getCode());
        noticeDTO.setTargetId(imagePO.getId());
        noticeDTO.setOwnerId(imagePO.getCreatedBy());
        noticeService.delete(noticeDTO);
        // 更新 Redis 用户信息（一定要在最后更新 Redis，否则事务回滚的话会读取到错误结果）
        List<Long> ids = findImageIdsByUserId(imageLikeDTO.getCreatedBy());
        applicationContext.publishEvent(new UserInfoIdsUpdatedEvent(this, ids,
                imageLikeDTO.getCreatedBy(), UserInfoIdsUpdatedEvent.Type.IMAGE_LIKE_IDS_UPDATED));
    }

    @Override
    public List<Long> findImageIdsByUserId(Long userId) {
        QueryWrapper<ImageLikePO> wrapper = new QueryWrapper<>();
        wrapper.select("image_id").eq("created_by", userId);
        List<ImageLikePO> imageLikePOS = imageLikeDao.selectList(wrapper);
        return Optional.ofNullable(imageLikePOS)
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(ImageLikePO::getImageId)
                .collect(Collectors.toList());
    }

    @Override
    public Pagination<ImageVO> queryImage(ImageLikeQuery imageLikeQuery) {
        Page<ImageLikePO> page = new Page<>(imageLikeQuery.getPage(), imageLikeQuery.getLimit());
        QueryWrapper<ImageLikePO> wrapper = new QueryWrapper<>();
        wrapper.eq("created_by", imageLikeQuery.getCreatedBy()).orderByDesc("create_time");
        Page<ImageLikePO> imageLikePOPage = imageLikeDao.selectPage(page, wrapper);
        List<Long> imageIds = Optional.ofNullable(imageLikePOPage.getRecords())
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(ImageLikePO::getImageId)
                .collect(Collectors.toList());
        if (imageIds.isEmpty()) {
            return Pagination.emptyPage(imageLikeQuery.getPage(), imageLikeQuery.getLimit());
        }
        List<ImageVO> imageVOS = Optional.ofNullable(imageDao.selectBatchIds(imageIds))
                .map(List::stream)
                .orElseGet(Stream::empty)
                .map(i -> {
                    Set<String> set = imageLabelService.getLabelsByImageId(i.getId());
                    return imageConverter.convertToVO(i, ArrayUtil.toArray(set, String.class));
                })
                .collect(Collectors.toList());

        Pagination<ImageVO> pagination = new Pagination<>();
        pagination.setCurrent(imageLikePOPage.getCurrent());
        pagination.setSize(imageLikePOPage.getSize());
        pagination.setTotal(imageLikePOPage.getTotal());
        pagination.setRecords(imageVOS);
        return pagination;
    }
}
